use std::{any::Any, collections::HashMap, fmt::Debug};

use common_base::sqlx::MySqlConnection;

use crate::{
    data::{DataObject, ParamObject, ResponseObject, RowField, RowsetOwner},
    db_manager::DbConnection,
};

use super::service_component_manager::ServiceComponentRunType;

pub trait ServiceContext: Any + Debug + Send {
    fn get_service_context_base(&self) -> &ServiceContextBase;
    fn get_service_context_base_mut(&mut self) -> &mut ServiceContextBase;

    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any;

    fn get_param_object(&self) -> &ParamObject {
        self.get_service_context_base().get_param_object()
    }
    fn get_response_object_mut(&mut self) -> &mut ResponseObject {
        self.get_service_context_base_mut()
            .get_response_object_mut()
    }

    fn get_connection_mut(&mut self) -> Option<&mut MySqlConnection> {
        self.get_service_context_base_mut()
            .get_db_connection_mut()
            .and_then(|db_conn| db_conn.get_conn_mut())
    }

    /// we you need Take connection, for example to close connection, use this function
    fn take_connection(&mut self) -> Option<MySqlConnection> {
        self.get_service_context_base_mut()
            .get_db_connection_mut()
            .map(|db_conn| db_conn.take_connection())
    }

    /// if we do not restore connection, the connection will be Drop. Other plugin will lose their dbConnection.
    fn restore_connection(&mut self, conn: Option<MySqlConnection>) {
        self.get_service_context_base_mut()
            .get_db_connection_mut()
            .map(|db_conn| db_conn.restore_connection(conn));
    }

    // fn set_auto_connection(&mut self, auto_connection: bool) {
    //     self.get_service_context_base_mut()
    //         .get_param_object_mut()
    //         .auto_connection = auto_connection;
    // }

    fn get_value_by_param_name(&self, key: &str) -> &str {
        self.get_service_context_base().get_value_by_param_name(key)
    }

    fn get_int_by_param_name(&self, key: &str, default: i32) -> i32 {
        self.get_service_context_base()
            .get_int_by_param_name(key, default)
    }

    fn get_value_by_env_name(&self, key: &str) -> &str {
        self.get_service_context_base().get_value_by_env_name(key)
    }
}

#[derive(Debug)]
pub struct ServiceContextBase {
    pub(crate) param_object: ParamObject,
    pub(crate) data_object: Option<DataObject>,
    /*customObject,  addonObject, */
    pub(crate) connection: Option<DbConnection>,
    pub(crate) response_object: ResponseObject,
    pub(crate) self_rowset: RowsetOwner,
    pub(crate) run_type: ServiceComponentRunType,
}

impl ServiceContextBase {
    pub fn new(
        param_object: ParamObject,
        data_object: Option<DataObject>,
        /*customObject,  addonObject, */
        connection: Option<DbConnection>,
        response_object: ResponseObject,
        run_type: ServiceComponentRunType,
    ) -> Self {
        Self {
            param_object,
            data_object,
            connection,
            response_object,
            self_rowset: RowsetOwner::default(),
            run_type,
        }
    }

    pub fn get_param_object(&self) -> &ParamObject {
        &self.param_object
    }

    pub fn get_param_object_mut(&mut self) -> &mut ParamObject {
        &mut self.param_object
    }

    pub fn get_value_by_param_name(&self, key: &str) -> &str {
        self.param_object.get_value_by_param_name(key)
    }

    pub fn get_int_by_param_name(&self, key: &str, default: i32) -> i32 {
        self.param_object.get_int_by_param_name(key, default)
    }

    pub fn get_bool_by_param_name(&self, key: &str, default: bool) -> bool {
        self.param_object.get_bool_by_param_name(key, default)
    }

    pub fn get_value_by_env_name(&self, key: &str) -> &str {
        self.param_object.get_value_by_env_name(key)
    }

    pub fn get_data_object(&self) -> Option<&DataObject> {
        self.data_object.as_ref()
    }

    pub fn get_data_object_mut(&mut self) -> Option<&mut DataObject> {
        self.data_object.as_mut()
    }

    pub fn get_response_object(&self) -> &ResponseObject {
        &self.response_object
    }

    pub fn get_response_object_mut(&mut self) -> &mut ResponseObject {
        &mut self.response_object
    }

    pub fn get_db_connection_mut(&mut self) -> Option<&mut DbConnection> {
        self.connection.as_mut()
    }

    pub fn get_connection_mut(&mut self) -> Option<&mut MySqlConnection> {
        self.get_db_connection_mut()
            .and_then(|db_conn| db_conn.get_conn_mut())
    }

    pub fn contains_key(&self, key: &str) -> bool {
        self.self_rowset.contains_key(key)
    }

    pub fn put_object(&mut self, key: &str, object: RowField) {
        self.self_rowset.put_object(key, object);
    }

    pub fn get_object(&self, key: &str) -> Option<&RowField> {
        self.self_rowset.get_object(key)
    }

    pub fn get_object_mut(&mut self, key: &str) -> Option<&mut RowField> {
        self.self_rowset.get_object_mut(key)
    }
}
